home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / ISSUE15 / SYSTEM / ContextM.pas next >
Encoding:
Pascal/Delphi Source File  |  1996-10-03  |  11.0 KB  |  352 lines

  1. unit ContextM;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Ole2, ShellAPI;
  7.  
  8. type
  9.     IShellClassFactory = class (IClassFactory)
  10.     private
  11.         RefCount: LongInt;
  12.     public
  13.         constructor Create;
  14.         function QueryInterface (const iid: TIID; var obj): HResult; override;
  15.         function AddRef: LongInt; override;
  16.         function Release: LongInt; override;
  17.         function LockServer (fLock: Bool): HResult; override;
  18.         function CreateInstance (unknown: IUnknown; const iid: TIID; var obj): HResult; override;
  19.     end;
  20.  
  21. var
  22.     LockCount, ObjCount: Integer;
  23.  
  24. implementation
  25.  
  26. type
  27.     PCMInvokeCommandInfo = ^TCMInvokeCommandInfo;
  28.     TCMInvokeCommandInfo = record
  29.         cbSize: DWord;           { size of data structure }
  30.         fMask: DWord;            { bitwise combination of CMIC_xxx flags }
  31.         hwnd: HWnd;              { handle of window owning context menu }
  32.         lpVerb: LPCStr;          { command string or else menu ID in low word }
  33.         lpParameters: LPCStr;    { always NULL for custom context menus }
  34.         lpDirectory: LPCStr;     { always NULL for custom context menus }
  35.         nShow: Integer;          { for ShowWindow API call if used }
  36.         dwHotKey: DWord;         { optional hotkey - not used by us }
  37.         hIcon: THandle;          { icon handle - not used by us }
  38.     end;
  39.  
  40.     TContextMenuObject = class;
  41.  
  42.     IContextMenu = class (IUnknown)
  43.         function QueryContextMenu (Menu: hMenu; indexMenu, idCmdFirst, idCmdLast, uFlags: UInt): HResult; virtual; stdcall; abstract;
  44.         function InvokeCommand (lpici: PCMInvokeCommandInfo): HResult; virtual; stdcall; abstract;
  45.         function GetCommandString (idCmd, uType: UInt; var res: UInt; lpHint: LPSTR; cchMax: UInt): HResult; virtual; stdcall; abstract;
  46.     end;
  47.  
  48.     IShellExtInit = class (IUnknown)
  49.         function Initialize (pidlFolder: Pointer; pdobj: IDataObject; hKeyProgID: HKey): HResult; virtual; stdcall; abstract;
  50.     end;
  51.  
  52.     TOwnedContextMenu = class (IContextMenu)
  53.     private
  54.         owner: TContextMenuObject;
  55.     public
  56.         constructor Create (aOwner: TContextMenuObject);
  57.         function QueryInterface (const iid: TIID; var obj): HResult; override;
  58.         function AddRef: LongInt; override;
  59.         function Release: LongInt; override;
  60.         function QueryContextMenu (hMenu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UInt): HResult; override;
  61.         function InvokeCommand (lpici: PCMInvokeCommandInfo): HResult; override;
  62.         function GetCommandString (idCmd, uType: UInt; var res: UInt; pszName: LPStr; cchMax: UInt): HResult; override;
  63.   end;
  64.  
  65.     TOwnedShellExtInit = class (IShellExtInit)
  66.     private
  67.         owner: TContextMenuObject;
  68.     public
  69.         constructor Create (aOwner: TContextMenuObject);
  70.         function QueryInterface (const iid: TIID; var obj): HResult; override;
  71.         function AddRef: LongInt; override;
  72.         function Release: LongInt; override;
  73.         function Initialize (pidlFolder: Pointer; pdobj: IDataObject; hKeyProgID: HKey): HResult; override;
  74.     end;
  75.  
  76.     { This is the *REAL* class - it just wraps the two interface classes we need }
  77.  
  78.     TContextMenuObject = class (IUnknown)
  79.     private
  80.         fName: String;
  81.         RefCount: Integer;
  82.         iContextMenu: TOwnedContextMenu;
  83.         iShellExtInit: TOwnedShellExtInit;
  84.     public
  85.         constructor Create;
  86.         destructor Destroy; override;
  87.         function QueryInterface (const iid: TIID; var obj): HResult; override;
  88.         function AddRef: LongInt; override;
  89.         function Release: LongInt; override;
  90.     end;
  91.  
  92. {---------------------------------------------------------------------------}
  93. {   Class Factory Methods                                                   }
  94. {---------------------------------------------------------------------------}
  95.  
  96. constructor IShellClassFactory.Create;
  97. begin
  98.     inherited Create;
  99.     RefCount := 0;
  100. end;
  101.  
  102. function IShellClassFactory.AddRef: LongInt;
  103. begin
  104.     Inc (RefCount);
  105.     Result := RefCount;
  106. end;
  107.  
  108. function IShellClassFactory.Release: LongInt;
  109. begin
  110.     Dec (RefCount);
  111.     Result := RefCount;
  112.     if RefCount = 0 then Free;
  113. end;
  114.  
  115. function IShellClassFactory.LockServer (fLock: Bool): HResult;
  116. begin
  117.     { bump lock count as requested }
  118.     if fLock then Inc (LockCount) else Dec (LockCount);
  119.     Result := 0;
  120. end;
  121.  
  122. function IShellClassFactory.QueryInterface (const iid: TIID; var obj): HResult;
  123. begin
  124.     if IsEqualIID (iid, IID_IUnknown) or IsEqualIID (iid, IID_IClassFactory) then
  125.     begin
  126.         Pointer (obj) := self;
  127.         AddRef;
  128.         Result := 0;
  129.     end
  130.     else
  131.     begin
  132.         Pointer (obj) := Nil;
  133.         Result := E_NoInterface;
  134.     end;
  135. end;
  136.  
  137. function IShellClassFactory.CreateInstance (unknown: IUnknown; const iid: TIID; var obj): HResult;
  138. var
  139.     cmo: TContextMenuObject;
  140. begin
  141.     Pointer (obj) := Nil;
  142.     if unknown <> Nil then Result := class_e_NoAggregation
  143.     else
  144.     try
  145.         cmo := TContextMenuObject.Create;
  146.         Result := cmo.QueryInterface (iid, obj);
  147.         if Result < 0 then cmo.Free;
  148.     except
  149.         Result := E_OutOfMemory;
  150.     end;
  151. end;
  152.  
  153. {---------------------------------------------------------------------------}
  154. {   TContextMenuObject Methods                                              }
  155. {---------------------------------------------------------------------------}
  156.  
  157. constructor TContextMenuObject.Create;
  158. begin
  159.     inherited Create;
  160.     iContextMenu := TOwnedContextMenu.Create (self);
  161.     iShellExtInit := TOwnedShellExtInit.Create (self);
  162.     RefCount := 0;
  163.     Inc (ObjCount);
  164. end;
  165.  
  166. destructor TContextMenuObject.Destroy;
  167. begin
  168.     iContextMenu.Free;
  169.     iShellExtInit.Free;
  170.     Dec (ObjCount);
  171. end;
  172.  
  173. function TContextMenuObject.AddRef: LongInt;
  174. begin
  175.     Inc (RefCount);
  176.     Result := RefCount;
  177. end;
  178.  
  179. function TContextMenuObject.Release: LongInt;
  180. begin
  181.     Dec (RefCount);
  182.     Result := RefCount;
  183.     if RefCount = 0 then Free;
  184. end;
  185.  
  186. function TContextMenuObject.QueryInterface (const iid: TIID; var obj): HResult;
  187. const
  188.     { The interface ID's we can respond to }
  189.     IID_IContextMenu : TGUID = (D1:$000214E4; D2:$0000; D3:$0000; D4:($C0,$00,$00,$00,$00,$00,$00,$46));
  190.     IID_IShellExtInit: TGUID = (D1:$000214E8; D2:$0000; D3:$0000; D4:($C0,$00,$00,$00,$00,$00,$00,$46));
  191. begin
  192.     Result := 0;
  193.     if IsEqualIID (iid, IID_IUnknown) then
  194.     begin
  195.         Pointer (obj) := self;            { Wants IUnknown - return self }
  196.         AddRef;
  197.     end
  198.     else if IsEqualIID (iid, IID_IContextMenu) then
  199.     begin
  200.         Pointer (obj) := iContextMenu;    { Wants IContextMenu - return it }
  201.         AddRef;
  202.     end
  203.     else if IsEqualIID (iid, IID_IShellExtInit) then
  204.     begin
  205.         Pointer (obj) := iShellExtInit;   { Wants IShellExtInit - return it }
  206.         AddRef;
  207.     end
  208.     else
  209.     begin
  210.         Pointer (obj) := nil;
  211.         Result := E_NoInterface;
  212.     end;
  213. end;
  214.  
  215. {---------------------------------------------------------------------------}
  216. {   TOwnedShellExtInit Methods                                                  }
  217. {---------------------------------------------------------------------------}
  218.  
  219. constructor TOwnedShellExtInit.Create (aOwner: TContextMenuObject);
  220. begin
  221.     inherited Create;
  222.     owner := aOwner;
  223. end;
  224.  
  225. function TOwnedShellExtInit.QueryInterface (const iid: TIID; var obj): HResult;
  226. begin
  227.     Result := owner.QueryInterface (iid, obj);
  228. end;
  229.  
  230. function TOwnedShellExtInit.AddRef: LongInt;
  231. begin
  232.     Result := owner.AddRef;
  233. end;
  234.  
  235. function TOwnedShellExtInit.Release: LongInt;
  236. begin
  237.     Result := owner.Release;
  238. end;
  239.  
  240. function TOwnedShellExtInit.Initialize (pidlFolder: Pointer; pdobj: IDataObject; hKeyProgID: HKey): HResult;
  241. var
  242.     fmte: TFormatEtc;
  243.     medium: TStgMedium;
  244. begin
  245.     { Assume the worst ! }
  246.     Result := E_Fail;
  247.     if pdobj <> nil then
  248.     begin
  249.         fmte.cfFormat := cf_hDrop;
  250.         fmte.ptd := nil;
  251.         fmte.dwAspect := dvAspect_Content;
  252.         fmte.lindex := -1;
  253.         fmte.tymed := tymed_hGlobal;
  254.  
  255.         { Use the given IDataObject to get a list of filenames }
  256.         Result := pdobj.GetData (fmte, medium);
  257.         if Result < 0 then Result := E_Fail
  258.         { Ensure that only one file is selected }
  259.         else if DragQueryFile (HDrop (medium.hGlobal), UInt (-1), Nil, 0) = 1 then
  260.         begin
  261.             { Stash the filename }
  262.             SetLength (owner.fName, 512);
  263.             DragQueryFile (HDrop (medium.hGlobal), 0, PChar (owner.fName), 512);
  264.             Result := 0;
  265.         end
  266.         else Result := E_Fail;
  267.         ReleaseStgMedium (medium);
  268.     end;
  269. end;
  270.  
  271. {---------------------------------------------------------------------------}
  272. {   TOwnedContextMenu Methods                                               }
  273. {---------------------------------------------------------------------------}
  274.  
  275. constructor TOwnedContextMenu.Create (aOwner: TContextMenuObject);
  276. begin
  277.     inherited Create;
  278.     owner := aOwner;
  279. end;
  280.  
  281. function TOwnedContextMenu.QueryInterface (const iid: TIID; var obj): HResult;
  282. begin
  283.     Result := owner.QueryInterface (iid, obj);
  284. end;
  285.  
  286. function TOwnedContextMenu.AddRef: LongInt;
  287. begin
  288.     Result := owner.AddRef;
  289. end;
  290.  
  291. function TOwnedContextMenu.Release: LongInt;
  292. begin
  293.     Result := owner.Release;
  294. end;
  295.  
  296. { Add commands to a context menu }
  297. function TOwnedContextMenu.QueryContextMenu (hMenu: hMenu; indexMenu, idCmdFirst, idCmdLast, uFlags: UInt): HResult;
  298. begin
  299.     { add our new menu item }
  300.     InsertMenu (hMenu, IndexMenu, mf_String or mf_ByPosition, idCmdFirst, '&Mega Menu...');
  301.     { return number of items added }
  302.     Result := 1;
  303. end;
  304.  
  305. { Execute a given menu command }
  306. function TOwnedContextMenu.InvokeCommand (lpici: PCMInvokeCommandInfo): HResult;
  307. var
  308.     sz: array [0..255] of Char;
  309. begin
  310.     Result := E_Fail;
  311.     if HiWord (LongInt (lpici.lpVerb)) = 0 then
  312.     begin
  313.         if loWord (lpici.lpVerb) > 0 then Result := E_InvalidArg
  314.         else
  315.         begin
  316.             { Normally, you'd case out on the menu identifier here }
  317.             case loWord (lpici.lpVerb) of
  318.             0: begin
  319.                    wvsprintf (sz, 'You picked: %s', @owner.fName);
  320.                    MessageBox (lpici.hwnd, sz, 'My First Context Menu', mb_ok);
  321.                    Result := 0;
  322.                end;
  323.             end;
  324.         end;
  325.     end;
  326. end;
  327.  
  328. { Return a menu item hint string }
  329. function TOwnedContextMenu.GetCommandString (idCmd, uType: UInt; var res: UInt; pszName: LPStr; cchMax: UInt): HResult;
  330. const
  331.     gcs_HelpText = 1;      { Explorer is requesting a menu hint string }
  332. begin
  333.     { If uType = gcs_HelpText, return a menu hint string for Explorer }
  334.     Result := e_NotImpl;
  335.     if uType = gcs_HelpText then
  336.     begin
  337.         { Case out on the menu item }
  338.         case idCmd of
  339.             0: begin
  340.                    lstrcpy (pszName, 'My very first context menu item !');
  341.                    Result := 0;
  342.                end
  343.             else Result := E_InvalidArg;
  344.         end;
  345.     end;
  346. end;
  347.  
  348. initialization
  349.     LockCount := 0;
  350.     ObjCount := 0;
  351. end.
  352.